import os
import time
import httpx
from tqdm import tqdm
from threading import Thread
import configparser
import datetime
import delete_logs
import script_logs
import sys
import get_tencentcloud_cdb_id



cf = configparser.ConfigParser()
root_path = os.path.dirname(os.path.abspath(__file__))
configfile = os.path.join(root_path, 'config.ini')
cf.read(configfile)
#腾讯云API秘钥
SecretId = cf.get('tencent_api', 'SecretId')
SecretKey = cf.get('tencent_api', 'SecretKey')
#实例区域
Region = cf.get('tencent_api', 'Region')
#数据存放时间
FileDate = cf.get('tencent_api', 'FileDate')
#存放数据目录
Dirname = cf.get('tencent_api', 'Dirname')
#进程数
thread_num = int(cf.get('tencent_api', 'thread_num'))
log_path = os.path.join(root_path, 'log')


class DownloadFile(object):
    def __init__(self, download_url, Dirname, thread_num):
        """
        :param download_url: 文件下载连接
        :param Dirname: 文件存储目录
        :param thread_num: 开辟线程数量
        """
        self.download_url = download_url
        self.Dirname = Dirname
        self.thread_num = thread_num
        self.file_size = None
        self.cut_size = None
        self.tqdm_obj = None
        self.thread_list = []
        self.file_path = os.path.join(self.Dirname, file_pname)

    def downloader(self, database, thread_index, start_index, stop_index, retry=False):
        sub_path_file = "{}_{}".format(self.file_path, thread_index)
        if os.path.exists(sub_path_file):
            temp_size = os.path.getsize(sub_path_file)  # 本地已经下载的文件大小
            msg = "确认本地已经下载文件：%s" %(temp_size)
            logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
            logs.ScriptLogs()
            if not retry:
                self.tqdm_obj.update(temp_size)  # 更新下载进度条
                msg = "更新进度条"
                logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
                logs.ScriptLogs()
        else:
            # msg = "开始下载"
            # logs = slogs.logs(msg=msg, log_path=log_path, level='info')
            # logs.ScriptLogs()
            temp_size = 0
        if stop_index == '-': stop_index = ""
        headers = {'Range': 'bytes={}-{}'.format(start_index + temp_size, stop_index),
                   'Datebase': database, 'if-Range': database,
                   }
        down_file = open(sub_path_file, 'ab')
        try:
            with httpx.stream("GET", self.download_url, headers=headers) as response:
                num_bytes_downloaded = response.num_bytes_downloaded
                for chunk in response.iter_bytes():
                    if chunk:
                        down_file.write(chunk)
                        self.tqdm_obj.update(response.num_bytes_downloaded - num_bytes_downloaded)
                        num_bytes_downloaded = response.num_bytes_downloaded
        except Exception as e:
            msg = "Thread-{}:请求超时,尝试重连\n报错信息:{}".format(thread_index, e)
            logs = script_logs.logs(msg=msg, log_path=log_path, level='error')
            logs.ScriptLogs()
            self.downloader(database, thread_index, start_index, stop_index, retry=True)
        finally:
            down_file.close()
        return

    def get_file_size(self):
        """
        获取预下载文件大小和文件database
        :return:
        """
        msg = "获取预下载文件大小和文件"
        logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
        logs.ScriptLogs()
        with httpx.stream("GET", self.download_url) as response2:
            database = ''
            total_size = int(response2.headers["Content-Length"])
            for tltle in response2.headers.raw:
                if tltle[0].decode() == "Datebase":
                    database = tltle[1].decode()
                    break
        return total_size, database

    def cutting(self):
        """
        切割成若干份
        :param file_size: 下载文件大小
        :param thread_num: 线程数量
        :return:
        """
        cut_info = {}
        msg = "获取总个文件大小：%s" %(self.file_size)
        logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
        logs.ScriptLogs()
        cut_size = self.file_size // self.thread_num
        msg = "单个文件大小：%s" %(cut_size)
        logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
        logs.ScriptLogs()
        for num in range(1, self.thread_num + 1):
            if num != 1:
                cut_info[num] = [cut_size, cut_size * (num - 1) + 1, cut_size * num]
            else:
                cut_info[num] = [cut_size, cut_size * (num - 1), cut_size * num]
            if num == self.thread_num:
                cut_info[num][2] = '-'
        return cut_info, cut_size

    def write_file(self):
        """
        合并分段下载的文件
        :param file_path:
        :return:
        """
        msg = "合并分段下载文件"
        logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
        logs.ScriptLogs()
        if os.path.exists(self.file_path):
            if len(self.file_path) >= self.file_size:
                return
        with open(self.file_path, 'ab') as f_count:
            for thread_index in range(1, self.thread_num + 1):
                with open("{}_{}".format(self.file_path, thread_index), 'rb') as sub_write:
                    f_count.write(sub_write.read())
                # 合并完成删除子文件
                os.remove("{}_{}".format(self.file_path, thread_index))
        msg = "%s 下载完成" %(file_pname)
        logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
        logs.ScriptLogs()
        return

    def create_thread(self, database, cut_info):
        """
        开辟多线程下载
        :param file_path: 文件存储路径
        :param database: headers校验
        :param cut_info:
        :return:
        """
        msg = "多线程下载"
        logs = script_logs.logs(msg=msg, log_path=log_path, level='info')
        logs.ScriptLogs()
        for thread_index in range(1, self.thread_num + 1):
            thread = Thread(target=self.downloader,
                            args=(database, thread_index, cut_info[thread_index][1], cut_info[thread_index][2]))

            thread.setName('Thread-{}'.format(thread_index))
            thread.setDaemon(True)
            thread.start()
            self.thread_list.append(thread)

        for thread in self.thread_list:
            thread.join()
        return

    def check_thread_status(self):
        """
        查询线程状态。
        :return:
        """
        while True:
            for thread in self.thread_list:
                thread_name = thread.getName()
                if not thread.isAlive():
                    msg = "{}:已停止".format(thread_name)
                    logs = script_logs.logs(msg=msg, log_path=log_path, level='error')
                    logs.ScriptLogs()
            time.sleep(1)

    def create_data(self):
        if not os.path.exists(self.Dirname):
            os.mkdir(self.Dirname)
        return

    def main(self):
        # 平分几份
        self.create_data()
        self.file_size, database = self.get_file_size()
        # 按线程数量均匀切割下载文件
        cut_info, self.cut_size = self.cutting()
        # 下载文件名称
        # 创建下载进度条
        self.tqdm_obj = tqdm(total=self.file_size, unit_scale=True, desc=self.file_path.split('/')[-1],
                             unit_divisor=1024,
                             unit="B")
        # 开始多线程下载
        self.create_thread(database, cut_info)
        # 合并多线程下载文件
        self.write_file()
        return


if __name__ == '__main__':
    try:

        project_name = sys.argv[1]
        # 数据库实例ID
        ID = cf.get(project_name, 'ID')
        # 文件名
        FileName = cf.get(project_name, 'FileName')
        file_data = datetime.datetime.now().strftime('%Y-%m-%d')
        file_pname = FileName + '-' + file_data
        file_db = Dirname + file_pname
        if not os.path.isfile(file_db):
            emp = get_tencentcloud_cdb_id.tencentid(secret_id=SecretId, secret_key=SecretKey, region=Region, id=ID)
            download_url = emp.GetId()
            delbak = delete_logs.clean(file_url=Dirname, file_date=FileDate)
            delbak.DelFile()
            downloader = DownloadFile(download_url, Dirname, thread_num)
            downloader.main()
        else:
            msg = "%s 数据备份问题已经存在" %(file_pname)
            logs = script_logs.logs(msg=msg, log_path=log_path, level='error')
            logs.ScriptLogs()
            exit()
    except Exception as err:
        print(err)
        logs = script_logs.logs(msg=err, log_path=log_path, level='error')
        logs.ScriptLogs()
        msg = "用法：%s 第一个参数project_name: 如dsh" %(sys.argv[0])
        print(msg)
